package 热题100.图论.实现Trie前缀树_208_中等;
/*
Trie： 高效的存储和查找字符串集合的数据结构

请你实现 Trie 类：
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中，返回 true（即，在检索之前已经插入）；否则，返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ，返回 true ；否则，返回 false 。

示例：
输入
["trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // 返回 True
trie.search("app");     // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app");     // 返回 True

提示：
1 <= word.length, prefix.length <= 2000
word 和 prefix 仅由小写英文字母组成
insert、search 和 startsWith 调用次数 总计 不超过 3 * 104 次

思路：
    一种是Acwing的写法，一种是Java对象的写法，下一个节点直接new出来，而ACWing的写法就是下一个节点用下标p(idx)表示。
* */

class Trie {
    public static void main(String[] args) {
        Trie obj = new Trie();
        String[] word = {"trie", "insert", "search", "search", "startswith", "insert", "search"};
        String[] prefix = {"","apple","apple","app","app","app","app"};
        for (String t : word){
            obj.insert(t);
        }
        for (String t : word){
            boolean param_2 = obj.search(t);
            System.out.println(param_2);
        }
        System.out.println("jieshu");
        obj.insert("app");
        for (String t : prefix){
            boolean param_3 = obj.startsWith(t);
            System.out.println(param_3);
        }
    }
    private Trie[] son; // 记录下一个节点
    private boolean isEnd; // 当前节点是否是结束点
    public Trie() {
        son = new Trie[26]; // 下一个节点是26个英文字符中的一个
        isEnd = false; // 默认不是结尾
    }

    public void insert(String word) {
        Trie node = this; // 获取根节点
        for (int i = 0; i < word.length(); i ++){
            int u = word.charAt(i) - 'a';
            if (node.son[u] == null){
                node.son[u] = new Trie();
            }
            // node移动到子节点
            node = node.son[u];
        }
        // 当前节点是结束节点，如abc中的c
        node.isEnd = true;
    }

    public boolean search(String word) {
        Trie end = searchPrefix(word);
        // 如果返回的结果不是null并且结束表示为true，说明有该字母结尾的字符串
        return (end != null && end.isEnd);
    }

    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }

    public Trie searchPrefix(String prefix){
        Trie node = this;
        for (int i = 0; i < prefix.length(); i ++){
            int u = prefix.charAt(i) - 'a';
            if (node.son[u] == null) return null;
            node = node.son[u];
        }
        // 如果存在改前缀就返回该节点的结尾
        return node;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

/*public class Trie {
    int N = 100010;
    int[][] son = new int[N][26];
    int idx = 0;
    int[] cnt = new int[N]; // 表示以改节点为结尾的字符串的个数
    public static void main(String[] args) {
        Trie obj = new Trie();
        String[] word = {"trie", "insert", "search", "search", "startswith", "insert", "search"};
        String[] prefix = {"","apple","apple","app","app","app","app"};
        for (String t : word){
            obj.insert(t);
        }
        for (String t : word){
            boolean param_2 = obj.search(t);
            System.out.println(param_2);
        }
        System.out.println("jieshu");
        obj.insert("app");
        for (String t : prefix){
            boolean param_3 = obj.startsWith(t);
            System.out.println(param_3);
        }
    }

    public Trie() {

    }

    public void insert(String word) {
        int p = 0;
        for (int i = 0; i < word.length(); i ++){
            int u = word.charAt(i) - 'a';
            if (son[p][u] == 0){
                son[p][u] = ++idx;
            }
            // 这里的idx既是给节点分配下标，也是将上一个节点和下一个节点连接的桥梁，这里通俗为下一层
            p = son[p][u];
        }
        cnt[p] ++;
    }

    public boolean search(String word) {
        int p = 0;
        for (int i = 0; i < word.length(); i ++){
            int u = word.charAt(i) - 'a';
            if (son[p][u] == 0) return false;
            p = son[p][u];
        }
        if (cnt[p] != 0){
            return true;
        } else {
            return false;
        }
    }

    public boolean startsWith(String prefix) {
        int p = 0;
        for (int i = 0; i < prefix.length(); i ++){
            int u = prefix.charAt(i) - 'a';
            if (son[p][u] == 0) return false;
            p = son[p][u];
        }
        return true;
    }

}*/

